package com.ict.ycwl.pathcalculate.service.Impl;

import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.conditions.Wrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;

import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.ict.ycwl.pathcalculate.form.AddRouteFrom;
import com.ict.ycwl.pathcalculate.mapper.*;
import com.ict.ycwl.pathcalculate.pojo.*;
import com.ict.ycwl.pathcalculate.service.RouteService;
import com.ict.ycwl.pathcalculate.vo.TransitDepotRouteVO;
import com.ict.ycwl.pathcalculate.vo.RouteDataVO;
import com.ict.ycwl.pathcalculate.vo.RouteVO;
import com.ict.ycwl.pathcalculate.vo.details.*;
import lombok.extern.slf4j.Slf4j;
import org.apache.poi.hpsf.Decimal;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.math.BigDecimal;
import java.math.RoundingMode;
import java.sql.Timestamp;
import java.time.LocalDate;
import java.time.format.DateTimeFormatter;
import java.util.*;
import java.util.concurrent.CompletableFuture;
import java.util.stream.Collectors;
import java.util.stream.Stream;

/**
 * 路径计算service实现类
 */
@Slf4j
@Service
public class RouteServiceImpl extends ServiceImpl<RouteMapper, Route> implements RouteService {

    @Value("${route.version-limit:3}")
    private Integer versionLimit;

    @Autowired
    private AreaMapper areaMapper;

    @Autowired
    private RouteMapper routeMapper;

    @Autowired
    private AccumulationMapper accumulationMapper;

    @Autowired
    private StoreMapper storeMapper;

    @Autowired
    private CarMapper carMapper;

    @Autowired
    private TransitDepotMapper transitDepotMapper;

    /**
     * 获取路线详情，包含大区、路线、聚集区
     *
     * @return vo
     */
    @Override
    public List<AreaDetailsVO> getRouteDetails() {
        //1.获取大区列表
        List<Area> areaList = areaMapper.selectList(null);
        //2.将大区集合转换成大区VO集合并找到路线集合、聚集区集合
        List<AreaDetailsVO> areaDetailsVOS = new ArrayList<>();
        for (Area area : areaList) {
            //1.找到路线集合
            QueryWrapper<Route> routeQueryWrapper = new QueryWrapper<>();
            routeQueryWrapper.eq("area_id", area.getAreaId());
            List<Route> routeList = routeMapper.selectList(routeQueryWrapper);
            //2.将路线集合转换成路线VO集合
            List<RouteDetailsVO> routeDetailsVOS = new ArrayList<>();
            for (Route route : routeList) {
                //1.找到聚集区列表
                QueryWrapper<Accumulation> accumulationWrapper = new QueryWrapper<>();
                accumulationWrapper.eq("route_id", route.getRouteId());
                List<Accumulation> accumulationList = accumulationMapper.selectList(accumulationWrapper);
                //2.将聚集区列表转换成聚集区VO列表e
                List<AccumulationDetailsVO> accumulationDetailsVOS = new ArrayList<>();
                for (Accumulation accumulation : accumulationList) {
                    AccumulationDetailsVO accumulationDetailsVO = new AccumulationDetailsVO();
                    BeanUtils.copyProperties(accumulation, accumulationDetailsVO);
                    accumulationDetailsVOS.add(accumulationDetailsVO);
                }
                //3.创建路线VO类并赋值
                RouteDetailsVO routeDetailsVO = RouteDetailsVO.builder().routeId(route.getRouteId())
                        .routeName(route.getRouteName())
                        .accumulationList(accumulationDetailsVOS)
                        .isOpen(false).build();
                //4.添加进路线VOList
                routeDetailsVOS.add(routeDetailsVO);
            }
            //3.创建大区VO类并赋值
            AreaDetailsVO areaDetailsVO = AreaDetailsVO.builder().areaId(area.getAreaId())
                    .areaName(area.getAreaName())
                    .routeList(routeDetailsVOS).build();
            //4.添加进路线VOList
            areaDetailsVOS.add(areaDetailsVO);

        }
        return areaDetailsVOS;
    }

    /**
     * 获取聚集区下商户信息
     *
     * @return vo
     */
    @Override
    public List<StoreDetailsVO> getStoreDetails(Long accumulationId) {
        //1.根据accumulationId查询店铺信息
        QueryWrapper<Store> storeQueryWrapper = new QueryWrapper<>();
        storeQueryWrapper.eq("accumulation_id", accumulationId);
        List<Store> storeList = storeMapper.selectList(storeQueryWrapper);
        //2.转换成VO集合并返回
        List<StoreDetailsVO> storeDetailsVOS = new ArrayList<>();
        for (Store store : storeList) {
            StoreDetailsVO storeDetailsVO = new StoreDetailsVO();
            BeanUtils.copyProperties(store, storeDetailsVO);
            storeDetailsVOS.add(storeDetailsVO);
        }
        return storeDetailsVOS;
    }


    /**
     * 获取地图数据（外层）
     *
     * @return vo
     */
    @Override
    public List<RouteVO> getMapData() {
        QueryWrapper<Route> routeQueryWrapper = new QueryWrapper<>();
        routeQueryWrapper.eq("is_delete", 0);
        List<Route> routeList = routeMapper.selectList(routeQueryWrapper);
        List<RouteVO> routeVOList = new ArrayList<>();
        for (Route route : routeList) {
            List<Map<String, Double>> polylineMap = getPolylineMap(route.getPolyline());
            List<Map<String, Double>> convexMap = getPolylineMap(route.getConvex());
            RouteVO routeVO = new RouteVO();
            BeanUtils.copyProperties(route, routeVO);
            routeVO.setPolyline(polylineMap);
            routeVO.setConvex(convexMap);
            routeVOList.add(routeVO);
        }
        return routeVOList;
    }

    /**
     * 获取大区历史路径列表数据
     *
     * @return vo
     */
    @Override
    public List<TransitDepotRouteVO> getTransitDepotRouteData() {
        List<TransitDepot> transitDepots = transitDepotMapper.selectList(null);
        List<TransitDepotRouteVO> transitDepotRouteVOList = new ArrayList<>();
        for (TransitDepot transitDepot : transitDepots) {
            QueryWrapper<Car> carQueryWrapper = new QueryWrapper<>();
            carQueryWrapper.eq("transit_depot_id", transitDepot.getTransitDepotId());
            List<Car> cars = carMapper.selectList(carQueryWrapper);

            TransitDepotRouteVO transitDepotRouteVO = TransitDepotRouteVO.builder().transitDepotName(transitDepot.getTransitDepotName())
                    .licensePlateNumberList
                            (cars.stream().map(Car::getLicensePlateNumber).collect(Collectors.toList()))
                    .transitDepotId(transitDepot.getTransitDepotId())
                    .build();
            transitDepotRouteVOList.add(transitDepotRouteVO);
        }
        return transitDepotRouteVOList;


    }

    /**
     * 获取路径详细数据
     *
     * @param transitDepotId 中转站ID
     * @param routeName      路线名称
     * @return vo
     */
    @Override
    public RouteDataVO getRouteData(Long transitDepotId, String routeName) {
        QueryWrapper<Route> routeQueryWrapper = new QueryWrapper<>();
        routeQueryWrapper.eq("transit_depot_id", transitDepotId);
        routeQueryWrapper.eq("route_name", routeName);
        Route route = routeMapper.selectOne(routeQueryWrapper);
        if (route == null) {
            return null;
        }
        RouteDataVO routeDataVO = getRouteDataVO(route);


        return routeDataVO;

    }

    /**
     * 添加路线数据（替换）
     *
     * @param from 请求参数
     */
    @Override
    @Transactional
    public String addRoute(List<AddRouteFrom> from) {
        // 获取所有中转站ID
        List<Long> transitDepotIdList = from.stream().map(AddRouteFrom::getTransitDepotId).distinct().collect(Collectors.toList());

        // 0. 获取路线版本号
        Map<Long, Integer> versionNum = new HashMap<>();

        // 获取当前日期
        LocalDate currentDate = LocalDate.now();
        // 创建日期格式化器
        DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd");
        // 格式化日期并输出
        String todayDate = currentDate.format(formatter);

        // 获取当前日期保存路径次数
        for (Long transitDepotId : transitDepotIdList) {
            int versionExitNum = getRouteVersionNum(todayDate, transitDepotId);

            // 查看是否超过限制次数
            if (versionExitNum < versionLimit) {
                versionNum.put(transitDepotId, ++versionExitNum);
            } else {
                return "今日保存次数已达上限";
            }
        }

        //1.假删除旧路径
        //1.1.通过大区ID找到旧路径
        QueryWrapper<Route> routeQueryWrapper = new QueryWrapper<>();
        // 通过中转站ID值进行匹配
        routeQueryWrapper.in("transit_depot_id", transitDepotIdList);
        Route routeUpdate = Route.builder().isDelete(true).build();
        //1.2.假删除
        routeMapper.update(routeUpdate, routeQueryWrapper);


        //2.保存新路径
        for (AddRouteFrom addRouteFrom : from) {
            //2.1.将坐标字符串Map转换成字符串
            String polylineString = getPolylineString(addRouteFrom.getPolyline());
            String convexString = getPolylineString(addRouteFrom.getConvex());
            //2.2.转换路径名称
            Integer versionSaveNumber = versionNum.get(addRouteFrom.getTransitDepotId());
            String routeName = addRouteFrom.getRouteName() + String.format("-%d.0", versionSaveNumber);
            //2.3.转换为实体
            Route route = Route.builder().polyline(polylineString)
                    .convex(convexString)
                    .routeName(routeName)
                    .cargoWeight(addRouteFrom.getCargoWeight())
                    .distance(addRouteFrom.getDistance())
                    .areaId(addRouteFrom.getAreaId())
                    .transitDepotId(addRouteFrom.getTransitDepotId())
                    .createTime(new Timestamp(System.currentTimeMillis()))
                    .updateTime(new Timestamp(System.currentTimeMillis()))
                    .versionNumber(versionSaveNumber)
                    .isDelete(false)
                    .build();
            //2.4.保存
            routeMapper.insert(route);
            //2.5 异步调用 保存工作时长
            CompletableFuture.runAsync(() -> {
                getWorkTime(route.getRouteId());
            });

            //2.6 异步调用 更新聚集区和商铺对应的路线Id
            CompletableFuture.runAsync(() -> {
                setAccumulationAndStoreRouteId(route.getRouteId());
            });

        }

        return null;
    }

    /**
     * 设置聚集区和商铺对应的路线Id
     * @param routeId
     */
    public void setAccumulationAndStoreRouteId(Long routeId){
        Route route = routeMapper.selectById(routeId);
        List<Map<String, Double>> polylineMap = getPolylineMapTmp(route.getPolyline());
        // 通过poly遍历查找每个Accumulation  -> Accumulation数组
        List<Accumulation> accumulationList = new ArrayList<>();
        for (Map<String, Double> map : polylineMap) {
            // 设置queryWrapper
            Double longitude = map.get("longitude");
            Double latitude = map.get("latitude");
            QueryWrapper<Accumulation> queryWrapper = new QueryWrapper<>();
            queryWrapper.eq("longitude", longitude);
            queryWrapper.eq("latitude", latitude);

            Accumulation accumulation = null;
            try {
                accumulation = accumulationMapper.selectList(queryWrapper).get(0);
            } catch (Exception e) {
                continue;
            }
            if (accumulation != null) {
                accumulationList.add(accumulation);
            }
        }
        // 遍历 Accumulation数组
        for (Accumulation accumulation : accumulationList) {
            // 更新Accumulation的routeId
            Accumulation updateAccumulation = Accumulation.builder()
                    .accumulationId(accumulation.getAccumulationId()).routeId(routeId).build();
            accumulationMapper.updateById(updateAccumulation);

            // 设置Accumulation下所有Store的RouteId
            QueryWrapper<Store> queryWrapper = new QueryWrapper<>();
            queryWrapper.eq("accumulation_id",accumulation.getAccumulationId());
            List<Store> storeList = storeMapper.selectList(queryWrapper);
            for (Store store : storeList) {
                Store updateStore = Store.builder().storeId(store.getStoreId())
                        .routeId(routeId).build();
                storeMapper.updateById(updateStore);
            }
        }

    }

    /**
     * 转换 路线坐标点串-获取字符串格式
     *
     * @param mapList
     * @return String
     */
    private static String getPolylineString(List<Map<String, Double>> mapList) {
        List<String> polylineList = new ArrayList<>();
        int size = mapList.size();
        StringBuilder s = new StringBuilder();
        for (Map<String, Double> map : mapList) {
            String polylineAlone = map.get("longitude") + "," + map.get("latitude");
            if (size != 1) {
                polylineList.add(polylineAlone + ";");
            } else {
                polylineList.add(polylineAlone);
            }

            size--;
        }

        for (String string : polylineList) {
            s.append(string);
        }
        return s.toString();
    }

    /**
     * 转换 路线坐标点串-获取Map格式
     *
     * @param
     * @return
     */
    public List<Map<String, Double>> getPolylineMap(String str) {
        //转换polyline数据
        List<Map<String, Double>> list = new ArrayList<>();

        //切割；
        String[] splitOne = str.split(";");
        //切割，
        for (String s : splitOne) {
            String[] splitTwo = s.split(",");
            Map<String, Double> map = new HashMap<>();
            map.put("latitude", Double.parseDouble(splitTwo[1]));
            map.put("longitude", Double.parseDouble(splitTwo[0]));
            list.add(map);
        }
        return list;
    }

    /**
     * 转换 路线坐标点串-获取Map格式
     *
     * @param
     * @return
     */
    private static List<Map<String, Double>> getPolylineMapTmp(String str) {
        //转换polyline数据
        List<Map<String, Double>> list = new ArrayList<>();

        //切割；
        String[] splitOne = str.split(";");
        //切割，
        for (int i = 1; i < splitOne.length; i++) {
            if (i == splitOne.length - 1) {
                break;
            }
            String[] splitTwo = splitOne[i].split(",");
            Map<String, Double> map = new HashMap<>();
            map.put("latitude", Double.parseDouble(splitTwo[1]));
            map.put("longitude", Double.parseDouble(splitTwo[0]));
            list.add(map);
        }
        return list;
    }

    /**
     * 获取路线版本号
     *
     * @param date
     * @param transitDepotId
     * @return
     */
    @Override
    public List<String> getRouteVersion(String date, Long transitDepotId) {
        int routeVersionNum = getRouteVersionNum(date, transitDepotId);

        // 处理数据
        List<String> versionlist = new ArrayList<>();
        for (int i = 1; i <= routeVersionNum; i++) {
            versionlist.add(i + ".0");
        }

        return versionlist;
    }

    /**
     * 获取路线版本号数
     *
     * @param date
     * @param transitDepotId
     * @return
     */
    private int getRouteVersionNum(String date, Long transitDepotId) {
        QueryWrapper<Route> versionWrapper = new QueryWrapper<>();
        versionWrapper.like("create_time", date);
        versionWrapper.eq("transit_depot_id", transitDepotId);
        List<Route> versionList = routeMapper.selectList(versionWrapper);
        List<Integer> versionExitNum = versionList.stream().map(Route::getVersionNumber).distinct().collect(Collectors.toList());
        return versionExitNum.size();
    }

    @Override
    public BigDecimal getWorkTime(Long routeId) {
        // T2
        // 根据routeId获取路线poly
        Route route = routeMapper.selectById(routeId);
        //List<Map<String, Double>> polylineMap = getPolylineMap(route.getPolyline());
        List<Map<String, Double>> polylineMap = getPolylineMapTmp(route.getPolyline());
        // 通过poly遍历查找每个store  -> Store数组
        List<Store> storeList = new ArrayList<>();
        for (Map<String, Double> map : polylineMap) {
            Double longitude = map.get("longitude");
            Double latitude = map.get("latitude");
            System.out.println(longitude + "," + latitude);
            QueryWrapper<Store> queryWrapper = new QueryWrapper<>();
            queryWrapper.eq("longitude", longitude);
            queryWrapper.eq("latitude", latitude);
            Store store = null;
            try {
                store = storeMapper.selectList(queryWrapper).get(0);
            } catch (Exception e) {
                continue;
            }
            if (store != null) {
                storeList.add(store);
            }
        }

        // 遍历store数组 算出两个store数组之间的距离并除以 相应的速度  接着累加
        BigDecimal t2 = new BigDecimal("0");
        for (int i = 0; i < storeList.size(); i++) {
            if (i == storeList.size() - 1) {
                break;
            }

            Store store = storeList.get(i);
            Store nextStore = storeList.get(i + 1);

            // 查看两个商铺的类型得   速度
            int speed;
            if (store.getLocationType().equals(nextStore.getLocationType())) {
                if (storeList.get(i).getLocationType().equals("0")) {
                    // 城区 -> 城区
                    speed = 30;
                } else {
                    // 乡镇 -> 乡镇
                    speed = 50;
                }
            } else {
                // 城区 -> 乡镇 或 乡镇 -> 城区
                speed = 40;
            }

            // 查看俩个商铺的距离
            double distance = calculateEuclideanDistance(new double[]{store.getLongitude(), store.getLatitude()}
                    , new double[]{nextStore.getLongitude(), nextStore.getLatitude()});

            // 距离除以速度 并t2累加
            BigDecimal speedDecimal = new BigDecimal(speed);
            BigDecimal distanceDecimal = new BigDecimal(distance).multiply(new BigDecimal("100"));
            BigDecimal divide = distanceDecimal.divide(speedDecimal, 2, RoundingMode.HALF_UP);
            BigDecimal multiplyHour = divide.multiply(new BigDecimal("60"));
            t2 = t2.add(multiplyHour);
        }

        // T3
        // 统计store集合中accumulationId 计算城区和乡镇的商铺总和
        int cityNum = 0;
        int countryNum = 0;
        List<Long> accumulationIdList = storeList.stream()
                .map(Store::getAccumulationId)
                .distinct()
                .collect(Collectors.toList());
        for (Long accumulationId : accumulationIdList) {
            QueryWrapper<Store> queryWrapper = new QueryWrapper<>();
            queryWrapper.eq("accumulation_id", accumulationId);
            List<Store> list = storeMapper.selectList(queryWrapper);
            for (Store store : list) {
                if ("0".equals(store.getLocationType())) {
                    cityNum++;
                } else {
                    countryNum++;
                }
            }
        }
        System.out.println("计算经过聚集区/打卡点数：" + storeList.size());
        System.out.println("T1(装车时长)：" + 40);
        System.out.println("T2（途中时长）：" + t2);
        System.out.println("城区商铺总数：" + cityNum);
        System.out.println("乡镇商铺总数：" + countryNum);
        BigDecimal a = new BigDecimal(cityNum).multiply(new BigDecimal("2.34"));
        BigDecimal b = new BigDecimal(countryNum).multiply(new BigDecimal("2.9"));
        System.out.println("城区商铺时长：" + a);
        System.out.println("乡镇商铺时长：" + b);

        BigDecimal t3 = a.add(b);
        System.out.println("T3（卸货配送时长）：" + t3);

        // T1+T2+T3
        BigDecimal t1 = new BigDecimal("40");
        BigDecimal t = t1.add(t2).add(t3);
        System.out.println("T（总时长）：" + t);

        // 更新路线工作时长
        if (!route.getIsDelete()){
            Route update = Route.builder().routeId(routeId).workTime(t.toString()).build();
            this.updateById(update);
        }

        return t;
    }



    /**
     * 两点之间欧氏距离的计算方法
     */
    private double calculateEuclideanDistance(double[] point1, double[] point2) {
        double sum = 0.0;
        for (int i = 0; i < point1.length; i++) {
            sum += Math.pow(point1[i] - point2[i], 2);
        }
        return Math.sqrt(sum);
    }

    @Override
    public RouteDataVO getRouteDataVO(Route route) {
        RouteDataVO routeDataVO = new RouteDataVO();
        BeanUtils.copyProperties(route, routeDataVO);
        //转换路线坐标点串
        List<Map<String, Double>> map = getPolylineMap(route.getPolyline());
        routeDataVO.setPolyline(map);
        if (route.getConvex() != null) {
            map = getPolylineMap(route.getConvex());
        }
        routeDataVO.setConvex(map);
        // 获取工作时长
        String workTime = route.getWorkTime();
        if (StrUtil.isEmpty(workTime)){
            workTime = getWorkTime(route.getRouteId()).toString();
        }
        routeDataVO.setWorkTime(workTime);
        return routeDataVO;
    }

}
